﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Publicuse.Util
{
    public class ReflectionToolUtil
    {
        /// <summary>
        /// 拷贝对象的属性值
        /// </summary>
        /// <param name="objDestination">用来承接值的对象实例</param>
        /// <param name="objSource">要被用来获取值的原始对象</param>
        public static void CopyProperty(object objDestination, object objSource)
        {
            if (objDestination == null || objSource == null)
                return;

            Type tDestination = objDestination.GetType();
            Type tSource = objSource.GetType();

            PropertyInfo[] arrPropertyInfoDestination = tDestination.GetProperties();
            PropertyInfo[] arrPropertyInfoSource = tSource.GetProperties();

            // 将原始对象的属性保存到 Hashtable 中，属性名为 Hashtable 的键值
            Hashtable hstblPropertyInfoSource = new Hashtable();
            foreach (PropertyInfo pi in arrPropertyInfoSource)
            {
                hstblPropertyInfoSource.Add(pi.Name.ToLower(), pi);
            }

            // 循环目标类型属性，假如目标类型中的属性名和原始类型属性名有相同的则将原始实例的属性值赋给目标对象对应的属性
            foreach (PropertyInfo pp in arrPropertyInfoDestination)
            {
                if (hstblPropertyInfoSource.ContainsKey(pp.Name.ToLower()))
                {
                    try
                    {
                        object objSourceValue = ((PropertyInfo)hstblPropertyInfoSource[pp.Name.ToLower()]).GetValue(objSource);
                        pp.SetValue(objDestination, objSourceValue);
                    }
                    catch // 暂时先吃掉类型不对等情况
                    { }
                }
            }
        }

        /// <summary>
        /// 将 DataTable 数据通过反射转换为 具体的类实例集合
        /// (处理思路是：从左到右遍历表的列名，同时对比T类型是否有相同名字的公共属性，假如有就将本行本列的值付给该属性。
        /// 所以请注意：假如该DataTable中有名字相同的列名时，最后那个列的值将成为T实例的最终属性值。)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dtl"></param>
        /// <returns></returns>
        public static List<T> ConvertTableToT<T>(DataTable dtl)
        {
            Type type = typeof(T);// Type.GetType("类的完全限定名"); 
            object obj = type.Assembly.CreateInstance(type.FullName);

            Hashtable arrPropertyInfo = ReflectionObjectPoolUtil.GetProperties(obj);

            int colCount = dtl.Columns.Count;
            int nRowCount = dtl.Rows.Count;

            List<T> lst = new List<T>();
            for (int i = 0; i < nRowCount; i++)
            {
                DataRow row = dtl.Rows[i];
                for (int j = 0; j < colCount; j++)
                {
                    string sColumnName = dtl.Columns[j].ColumnName;
                    if (!arrPropertyInfo.Contains(sColumnName.ToLower()))
                        continue;

                    PropertyInfo pi = arrPropertyInfo[sColumnName.ToLower()] as PropertyInfo;// 获取名字相同的属性 
                    if (pi != null)
                    {
                        Object objCellValue = row[sColumnName];
                        objCellValue = objCellValue == DBNull.Value ? null : objCellValue; // 将 DBNull.Value 值转换为null值
                        if (objCellValue == null) // 如果是空值直接赋值并返回进入下个循环，不用进行下边的可空枚举验证操作。
                        {
                            pi.SetValue(obj, objCellValue);
                            continue;
                        }

                        // 空枚举验证操作
                        Type t = Nullable.GetUnderlyingType(pi.PropertyType); // 获取可以为 null 的类型的基础类型（主要方便对可空的枚举类型进行操作）
                        if (t != null && t.IsEnum && Enum.IsDefined(t, objCellValue)) //属性类型是否表示枚举
                        {
                            object enumName = Enum.ToObject(t, objCellValue);
                            pi.SetValue(obj, enumName); //获取枚举值，设置属性值
                        }
                        else
                        {
                            SetPropertyValue(pi, obj, objCellValue);
                        }
                    }
                }
                lst.Add((T)obj);
                obj = type.Assembly.CreateInstance(type.FullName); // 从新创建一个对象
            }
            return lst;
        }

        /// <summary>
        /// 将 DataTable 数据通过反射转换为 具体的类实例集合并返回第一个实例
        /// (处理思路是：从左到右遍历表的列名，同时对比T类型是否有相同名字的公共属性，假如有就将本行本列的值付给该属性。
        /// 所以请注意：假如该DataTable中有名字相同的列名时，最后那个列的值将成为T实例的最终属性值。)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dtl"></param>
        /// <returns></returns>
        public static T GetFirstByTableT<T>(DataTable dtl)
        {
            List<T> lstT = ConvertTableToT<T>(dtl);
            if (lstT == null || lstT.Count < 1)
                return default(T);
            return lstT[0];
        }


        /// <summary>
        /// 设置对象的属性值
        /// </summary>
        /// <param name="pInfo"></param>
        /// <param name="obj"></param>
        /// <param name="Value"></param>
        static void SetPropertyValue(PropertyInfo pInfo, object obj, object Value)
        {
            switch (pInfo.PropertyType.Name)
            {
                case "Boolean":
                    pInfo.SetValue(obj, Convert.ToBoolean(Value));
                    break;
                case "Int64":
                    pInfo.SetValue(obj, Convert.ToInt64(Value));
                    break;
                case "Int32":
                    pInfo.SetValue(obj, Convert.ToInt32(Value));
                    break;
                case "Double":
                    pInfo.SetValue(obj, Convert.ToDouble(Value));
                    break;
                case "DateTime":
                    {
                        //// 过滤MySql.data.Types.MySqlDateTime Value.GetType().FullName
                        //if (Value.GetType().FullName == "MySql.data.Types.MySqlDateTime")
                        //{
                        //    if (((MySql.data.Types.MySqlDateTime)Value).IsValidDateTime)
                        pInfo.SetValue(obj, Convert.ToDateTime(Value));
                        //}
                    }
                    break;
                case "String":
                    //if (Value.GetType().Name == "DateTime") // 假如程序定义的属性是字符串类型，但是传进的值是DateTime类型，则将见时间转换为：yyyy-MM-dd HH:mm:ss 这种字符串格式
                    //    pInfo.SetValue(obj, ((DateTime)Value).ToString("yyyy-MM-dd HH:mm:ss"));
                    //else if (Value.GetType().FullName == "MySql.data.Types.MySqlDateTime")
                    //{
                    //    if (!((MySql.data.Types.MySqlDateTime)Value).IsValidDateTime) // 如果从数据库来的值不是有效的时间则设置为""
                    //        pInfo.SetValue(obj, "");
                    //    else
                    //        pInfo.SetValue(obj, Convert.ToDateTime(Value).ToString("yyyy-MM-dd HH:mm:ss"));
                    //}
                    //else
                    pInfo.SetValue(obj, Value.ToString());
                    break;
                default:
                    pInfo.SetValue(obj, Value);
                    break;
            }
        }


        /// <summary>
        /// 创建对象实例
        /// </summary>
        /// <param name="dllPath"></param>
        /// <param name="classFullName"></param>
        /// <returns></returns>
        public static object CreateInstance(string dllPath, string classFullName)
        {
            try
            {
                object obj = Assembly.Load(dllPath).CreateInstance(classFullName);//加载程序集，创建程序集里面的 命名空间.类型名 实例
                return obj;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }
}
